﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace IV.Core
{
    public class AppSettingServiceBackup
    {
        private static readonly Dictionary<ModuleItem, List<SettingItem>> _appSettingDict = new Dictionary<ModuleItem, List<SettingItem>>();

        /// <summary>
        /// 设置变更事件
        /// </summary>
        public static event Action? AppSettingUpdateEvent;

        public static ISettingPersistence SettingPersistence { get;set; }

        /// <summary>
        /// 获取所有设置信息
        /// </summary>
        /// <returns>所有设置信息</returns>
        public static Dictionary<ModuleItem, List<SettingItem>> GetAllAppSettingDict()
        {
            return Clone(_appSettingDict);
        }

        /// <summary>
        /// 加载所有设置信息
        /// </summary>
        /// <param name="settingDict">所有设置信息</param>
        public static void LoadSettings(Dictionary<ModuleItem, List<SettingItem>> settings)
        {
            foreach (var item in settings)
            {
                var settingList = GetLocalSettings(item.Key.ItemName);
                if (settingList != null)
                {
                    foreach (var tempSetting in item.Value)
                    {
                        var settingItem = GetLocalSetting(item.Key.ItemName, tempSetting.Name);
                        if (settingItem != null)
                            SetSetting(item.Key.ItemName, tempSetting.Name, tempSetting.Value);
                    }
                }
            }
            AppSettingUpdateEvent?.BeginInvoke(null, new EventArgs());
        }

        public static void RegisterSetting<T>(SettingItem settingItems, string moduleDiscribe = "")
        {
            RegisterSetting(typeof(T).Name, settingItems, moduleDiscribe);
        }

        public static void RegisterSetting(string settingModuleName, SettingItem settingItems, string moduleDiscribe = "")
        {
            if (string.IsNullOrWhiteSpace(settingModuleName))
                return;
            if (string.IsNullOrWhiteSpace(moduleDiscribe))
                moduleDiscribe = settingModuleName;
            if (!IsExitModule(settingModuleName))
                _appSettingDict.Add(new ModuleItem(settingModuleName, moduleDiscribe), new List<SettingItem>());
            if (!IsExitSetting(settingModuleName, settingItems.Name))
            {
                foreach (var item in _appSettingDict)
                    if (item.Key.ItemName.Equals(settingModuleName))
                        _appSettingDict[item.Key].Add(settingItems);
            }
        }

        public static void RegisterSetting<T>(List<SettingItem> settingItems, string moduleDiscribe = "")
        {
            foreach (var item in settingItems)
            {
                RegisterSetting(typeof(T).Name, item, moduleDiscribe);
            }
        }

        public static void RegisterSetting(string moduleName, List<SettingItem> settingItems, string moduleDiscribe = "")
        {
            foreach (var item in settingItems)
            {
                RegisterSetting(moduleName, item, moduleDiscribe);
            }
        }

        public static void SetSetting<T1,T2>(string settingName, T1 value)
        {
            SetSetting(typeof(T2).Name, settingName, value);
        }

        public static bool SetSetting<T>(string moduleName, string settingName, T value)
        {
            if (string.IsNullOrWhiteSpace(moduleName)||string.IsNullOrWhiteSpace(settingName)||value==null)
                return false;
            var settingItemType = value.GetType();
            if (!settingItemType.Equals(typeof(int)) &&
                !settingItemType.Equals(typeof(string)) &&
                !settingItemType.Equals(typeof(float)) &&
                !settingItemType.Equals(typeof(double)) &&
                !settingItemType.IsEnum)
                return false;

            var settingItem = GetLocalSetting(moduleName, settingName);
            if (settingItem != null)
            {
                if (!settingItem.Type.Equals(settingItemType))
                    return false;

                var settingItemTypeName = settingItem.Type.Name.ToLower();
                switch (settingItemTypeName)
                {
                    case "int":
                    case "int32":
                        IntSettingItem intSettingItem = (IntSettingItem)settingItem;
                        if (!value.GetType().Equals(intSettingItem.Value.GetType()))
                            return false;
                        var intValue = int.Parse(value.ToString());
                        intSettingItem.Value = int.Parse(value.ToString());
                        break;
                    case "float":
                        FloatSettingItem floatSettingItem = (FloatSettingItem)settingItem;
                        if (!value.GetType().Equals(floatSettingItem.Value.GetType()))
                            return false;
                        floatSettingItem.Value = float.Parse(value.ToString());
                        break;
                    case "double":
                        DoubleSettingItem doubleSettingItem = (DoubleSettingItem)settingItem;
                        if (!value.GetType().Equals(doubleSettingItem.Value.GetType()))
                            return false;
                        doubleSettingItem.Value = double.Parse(value.ToString());
                        break;
                    //case "enum":
                    //    EnumSettingItem enumSettingItem = (EnumSettingItem)settingItem;
                    //    if (value.GetType().Equals(enumSettingItem.Value.GetType()))
                    //        enumSettingItem.Value = Enum.Parse(enumSettingItem.EnumType,value.ToString());
                    //    break;
                    default:
                        if (!value.GetType().Equals(settingItem.Value.GetType()))
                            return false;
                        settingItem.Value = value.ToString();
                        break;
                }
                AppSettingUpdateEvent?.BeginInvoke(null, new EventArgs());
                SettingPersistence.SaveSettings(_appSettingDict);
                return true;
            }
            return false;
        }

        public static T1 GetSetting<T1,T2>(string settingName)
        {
            return GetSetting<T1>(typeof(T2).Name, settingName);
        }

        public static T GetSetting<T>(string moduleName, string settingName)
        {
            if (string.IsNullOrWhiteSpace(moduleName) || string.IsNullOrWhiteSpace(settingName))
                return default;
            var settingItem = GetLocalSetting(moduleName, settingName);
            //if (settingItem != null)
            //    if (settingItem.Value.GetType().Equals(typeof(T)))
            //        return (T)settingItem.Value;
            return default;
        }

        private static List<SettingItem> GetLocalSettings(string moduleName)
        {
            if (string.IsNullOrWhiteSpace(moduleName))
                return null;
            if (!IsExitModule(moduleName))
                return null;
            foreach (var item in _appSettingDict)
                if (item.Key.ItemName.Equals(moduleName))
                    return item.Value;
            return null;
        }

        private static SettingItem GetLocalSetting(string moduleName,string settingName)
        {
            if (string.IsNullOrWhiteSpace(moduleName))
                return null;
            if (!IsExitModule(moduleName))
                return null;
            var settings = GetLocalSettings(moduleName);
            if (settings == null)
                return null;

            foreach (var setting in settings)
                if (setting.Name.Equals(settingName))
                    return setting;
            return null;
        }

        private static bool IsExitModule(string moduleName)
        {
            if (string.IsNullOrWhiteSpace(moduleName))
                return false;
            foreach (var item in _appSettingDict)
            {
                if (item.Key.ItemName.Equals(moduleName))
                    return true;
            }
            return false;
        }

        private static bool IsExitSetting(string moduleName, string settingName)
        {
            if (string.IsNullOrWhiteSpace(moduleName))
                return false;
            if (!IsExitModule(moduleName))
                return false;
            if (GetLocalSetting(moduleName, settingName) != null)
                return true;
            return false;
        }

        private static T Clone<T>(T obj)
        {
            MemoryStream memoryStream = new();
            BinaryFormatter formatter = new();
            formatter.Serialize(memoryStream, obj);
            memoryStream.Position = 0;
            return (T)formatter.Deserialize(memoryStream);
        }

    }

    [Serializable]
    /// <summary>
    /// 基础设置项信息
    /// </summary>
    public class SettingItem
    {
        private string _moduleName;

        private string _permissionName;

        public string Name { get; set; }

        public bool IsVisible { get; set; } = true;

        public bool IsEnableChange
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_moduleName) || string.IsNullOrWhiteSpace(_permissionName))
                    return true;
                return AppPermissionService.HavePermission(_moduleName, _permissionName);
            }
        }

        public bool IsSerializable { get; set; } = false;

        public string Description { get; set; }

        private string _value;
        public string Value
        {
            get => _value;
            set
            {
                if (!IsEnableChange)
                    return;
                _value = value;
            }
        }

        public Type Type { get; set; } = typeof(string);

        public void SetPermission<T>(string permissionName)
        {
            _moduleName = typeof(T).Name;
            _permissionName = permissionName;
        }

        public void SetPermission(string permissionModuleName, string permissionName)
        {
            _moduleName = permissionModuleName;
            _permissionName = permissionName;
        }
    }

    [Serializable]
    public class IntSettingItem : SettingItem
    {
        public new bool IsSerializable { get => false; }

        public int UpperLimit { get; set; }

        public int LowerLimit { get; set; }

        private int _value;
        public new int Value
        {
            get => _value;
            set
            {
                if (!IsEnableChange)
                    return;
                if (UpperLimit==LowerLimit||LowerLimit>UpperLimit)
                    _value = value;
                if (value <= UpperLimit && value >= LowerLimit)
                    _value = value;
            }
        }

        public IntSettingItem(int upperLimit, int lowerLimit)
        {
            UpperLimit = upperLimit;
            LowerLimit = lowerLimit;
        }
    }

    [Serializable]
    public class FloatSettingItem : SettingItem
    {
        public new bool IsSerializable { get => false; }

        public float UpperLimit { get; set; }

        public float LowerLimit { get; set; }

        private float _value;
        public new float Value
        {
            get => _value;
            set
            {
                if (!IsEnableChange)
                    return;
                if (UpperLimit == LowerLimit || LowerLimit > UpperLimit)
                    _value = value;
                if (value <= UpperLimit && value >= LowerLimit)
                    _value = value;
            }
        }

        public FloatSettingItem(float upperLimit, float lowerLimit)
        {
            UpperLimit = upperLimit;
            LowerLimit = lowerLimit;
        }
    }

    [Serializable]
    public class DoubleSettingItem : SettingItem
    {
        public new bool IsSerializable { get => false; }

        public double UpperLimit { get; set; }

        public double LowerLimit { get; set; }

        private double _value;
        public new double Value
        {
            get => _value;
            set
            {
                if (!IsEnableChange)
                    return;
                if (UpperLimit == LowerLimit || LowerLimit > UpperLimit)
                    _value = value;
                if (value <= UpperLimit && value >= LowerLimit)
                    _value = value;
            }
        }

        public DoubleSettingItem(double upperLimit, double lowerLimit)
        {
            UpperLimit = upperLimit;
            LowerLimit = lowerLimit;
        }
    }

    [Serializable]
    public class EnumSettingItem : SettingItem
    {
        public new bool IsSerializable { get => false; }

        public Type EnumType { get; set; }

        private string _value;
        public new string Value
        {
            get => _value; set
            {
                if (!IsEnableChange)
                    return;
                _value = value;
            }
        }
    }

    public interface ISettingPersistence
    {
        public void Initialize();

        public Dictionary<ModuleItem, List<SettingItem>> LoadSettings();

        public void SaveSettings(Dictionary<ModuleItem, List<SettingItem>> settings);
    }

}
